import * as React from "react";

import {
  Routes,
  Route,
  Link,
  useNavigate,
  useLocation,
  Navigate,
  Outlet
} from "react-router-dom";

import { fakeAuthProvider } from "./auth";

export default function App() {
  return (
    <AuthProvider>
      <h1>Auth Example</h1>
      <p>
        This example demonstrates a simple login flow with three pages: a public
        page, a protected page, and a login page. In order to see the protected
        page, you must first login. Pretty standard stuff.
      </p>
      <p>
        First, visit the public page. Then, visit the protected page. You're not
        yet logged in, so you are redirected to the login page. After you login,
        you are redirected back to the protected page.
      </p>
      <p>
        Notice the URL change each time. If you click the back button at this
        point, would you expect to go back to the login page? No! You're already
        logged in. Try it out, and you'll see you go back to the page you
        visited just *before* logging in, the public page.
      </p>

      <Routes>
        <Route element={<Layout />}>
          <Route path="/" element={<PublicPage />} />
          <Route path="/login" element={<LoginPage />} />
          <Route
            path="/protected"
            element={
              <RequireAuth>
                <ProtectedPage />
              </RequireAuth>
            }
          />
        </Route>
      </Routes>

    </AuthProvider>
  );
}

function Layout() {
  return (
    <div>

      {/* 登录状态组件 */}
      <AuthStatus />

      {/* 两个超链接 */}
      <ul>
        <li>
          <Link to="/">Public Page</Link>
        </li>
        <li>
          <Link to="/protected">Protected Page</Link>
        </li>
      </ul>

      {/* 一个插槽 */}
      <Outlet />
    </div>
  );
}

// 全局AuthContext
let AuthContext = React.createContext(null);

function AuthProvider({ children }) {

  // 将user设置为state
  let [user, setUser] = React.useState(null);

  /* 登录函数 */
  let signin = (newUser, callback) => {
    return fakeAuthProvider.signin(() => {
      setUser(newUser);
      callback();
    });
  };

  /* 登出函数 */
  let signout = (callback) => {
    return fakeAuthProvider.signout(() => {
      setUser(null);
      callback();
    });
  };

  // 全局透传user, signin, signout
  let value = { user, signin, signout };

  /* 返回AuthContext.Provider组件 */
  return (
    <AuthContext.Provider value={value}>
      {children}
    </AuthContext.Provider>
  );
}

/* 返回AuthContext的全局透传的value */
function useAuth() {
  return React.useContext(AuthContext);
}

/* 根据全局透传的AuthContext值 返回不同界面 */
function AuthStatus() {

  // 得到全局透传的value 即auth数据
  let auth = useAuth();
  let navigate = useNavigate();

  /* 未登录时返回JSX提示未登录 */
  if (!auth.user) {
    return <p>You are not logged in.</p>;
  }

  /* 已登录时返回欢迎界面 */
  return (
    <p>
      Welcome {auth.user}!{" "}

      {/* 点击按钮注销 并导航回首页 */}
      <button
        onClick={() => {
          auth.signout(() => navigate("/"));
        }}
      >
        Sign out
      </button>
    </p>
  );

}

/* 高阶组件RequireAuth */
function RequireAuth({ children }) {

  // 得到AuthContext全局透传的数据(user+singin+signout)
  let auth = useAuth();
  let location = useLocation();

  /* 未登录则原地重定向到login页 */
  if (!auth.user) {
    return <Navigate to="/login" state={{ from: location }} replace />;
  }

  // 已登录则直接返回被包裹的组件
  return children;
}

/* 登录组件 */
function LoginPage() {

  // 用于跳转页面
  let navigate = useNavigate();

  // 用于获取location信息
  let location = useLocation();

  // 获取Context全局透传的登录信息（user+singin+signout）
  let auth = useAuth();

  // 获取跳转的来源页
  let from = location.state.from.pathname || "/";

  /* 执行登录 */
  function handleSubmit(event) {
    event.preventDefault();

    /* 从表单中获取username */
    let formData = new FormData(event.currentTarget);
    let username = formData.get("username");

    /* 执行登录并重新原地跳转来源页 */
    auth.signin(username, () => {
      navigate(from, { replace: true });
    });
    
  }

  /* 登陆表单JSX */
  return (
    <div>
      <p>You must log in to view the page at {from}</p>
      <form onSubmit={handleSubmit}>
        <label>
          Username: <input name="username" type="text" />
        </label>{" "}
        <button type="submit">Login</button>
      </form>
    </div>
  );
}

/* 公共页组件 */
function PublicPage() {
  return <h3>Public</h3>;
}

/* 受保护页组件 */
function ProtectedPage() {
  return <h3>Protected</h3>;
}
